package com.koron.sinoprobe.web.ledger.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.koron.bean.system.org.OrgBean;
import com.koron.bean.system.staff.vo.EamUser;
import com.koron.common.core.business.common.entity.TOrderNoMax;
import com.koron.common.core.business.common.service.TOrderNoMaxService;
import com.koron.common.core.config.ThreadLocalContext;
import com.koron.common.core.interceptor.OrderByInterceptor;
import com.koron.common.core.util.CodeTools;
import com.koron.common.core.util.OrgCacheUtils;
import com.koron.sinoprobe.config.Constants;
import com.koron.sinoprobe.web.common.CommonUtils;
import com.koron.sinoprobe.web.engineering.bean.query.SpBudgetReviewQuery;
import com.koron.sinoprobe.web.engineering.bean.vo.SpBudgetReviewVO;
import com.koron.sinoprobe.web.engineering.service.SpBudgetReviewService;
import com.koron.sinoprobe.web.engineering.service.SpEndAppraiseService;
import com.koron.sinoprobe.web.engineering.service.SpEstablishService;
import com.koron.sinoprobe.web.engineering.service.SpSettlementAuditService;
import com.koron.sinoprobe.web.ledger.bean.entity.SpProject;
import com.koron.sinoprobe.web.ledger.bean.enums.ProjectEnum;
import com.koron.sinoprobe.web.ledger.bean.query.SpProjectQuery;
import com.koron.sinoprobe.web.ledger.bean.query.SpReportQuery;
import com.koron.sinoprobe.web.ledger.bean.vo.KeyValue;
import com.koron.sinoprobe.web.ledger.bean.vo.ProjectOverviewStat;
import com.koron.sinoprobe.web.ledger.bean.vo.SpProjectVO;
import com.koron.sinoprobe.web.ledger.bean.vo.SpReportVO;
import com.koron.sinoprobe.web.ledger.mapper.SpProjectMapper;
import com.koron.sinoprobe.web.ledger.mapper.SpReportMapper;
import com.koron.sinoprobe.web.ledger.service.SpProjectEquipmentService;
import com.koron.sinoprobe.web.ledger.service.SpProjectService;
import com.koron.sinoprobe.web.plan.bean.entity.SpAssistDept;
import com.koron.sinoprobe.web.plan.bean.entity.SpMember;
import com.koron.sinoprobe.web.plan.bean.entity.SpPlanMilestone;
import com.koron.sinoprobe.web.plan.mapper.SpAssistDeptMapper;
import com.koron.sinoprobe.web.plan.mapper.SpMemberMapper;
import com.koron.sinoprobe.web.plan.mapper.SpPlanMilestoneMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.koron.ebs.mybatis.SessionFactory;
import org.koron.ebs.mybatis.TaskAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * @author twcao
 * @title: SpProjectServiceImpl
 * @projectName GDH_EAM_V2
 * @description: 专项项目service实现
 * @date 2021/12/114:08
 */
@Service
public class SpProjectServiceImpl implements SpProjectService {
    @Autowired
    private SpBudgetReviewService budgetReviewService;

    @Autowired
    private SpEndAppraiseService spEndAppraiseService;

    @Autowired
    private SpSettlementAuditService spSettlementAuditService;

    @Autowired
    private OrgCacheUtils orgCacheUtils;

    @Autowired
    private TOrderNoMaxService tOrderNoMaxService;

    @Autowired
    private SpEstablishService spEstablishService;

    @Autowired
    SpProjectEquipmentService spProjectEquipmentService;

    @Override
    @TaskAnnotation("getById")
    public SpProject getById(SessionFactory factory, String projectId) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.queryById(projectId);
    }

    @Override
    @TaskAnnotation("selectOne")
    public SpProjectVO selectOne(SessionFactory factory, String projectId) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        SpProjectVO spProject = mapper.queryDetailById(projectId);
        SpReportMapper reportMapper = factory.getMapper(SpReportMapper.class);
        List<SpReportVO> spReportVOS = reportMapper.queryList(new SpReportQuery().setProjectId(projectId));
        if (CollectionUtils.isEmpty(spReportVOS)) {
            return spProject;
        }
        BigDecimal invested = spReportVOS.stream().map(SpReportVO::getNoNullInvestment).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal budgetPrice;

        // 如果有预算审查单则查预算审查，否则查计划预算资金
        // 查询已审批完成、未删除的预算审查
        List<SpBudgetReviewVO> list = budgetReviewService.list(factory, new SpBudgetReviewQuery().setProjectId(projectId).setStatus(Arrays.asList(Constants.AppceptStatus.APPROVED)));
        if(!CollectionUtils.isEmpty(list)) {
            // 倒序排列，如果有多个，取最新创建的预算审查预算审核价
            Optional<SpBudgetReviewVO> first = list.stream().sorted(Comparator.comparing(SpBudgetReviewVO::getCreateTime).reversed()).findFirst();
            if(first.get().getBudgetAuditPrice() != null) {
                budgetPrice = new BigDecimal(first.get().getBudgetAuditPrice());
                if(!CollectionUtils.isEmpty(spReportVOS) && budgetPrice.intValue() != 0) {
                    // 100 * 项目汇报的投资总额 / 预算审核价 = 预算完成率，保留两位小数
                    spProject.setBudgetProgress((invested.multiply(new BigDecimal(100)).divide(budgetPrice, 2, BigDecimal.ROUND_HALF_UP)));
                }
            }
        }
        if (spProject.getBudgetProgress() == null) {
            budgetPrice = spProject.getBudget();
            if(budgetPrice != null && !CollectionUtils.isEmpty(spReportVOS) && budgetPrice.intValue() != 0) {
                // 100 * 项目汇报的投资总额 / 预算审核价 = 预算完成率，保留两位小数
                spProject.setBudgetProgress((invested.multiply(new BigDecimal(100)).divide(budgetPrice, 2, BigDecimal.ROUND_HALF_UP)));
            }

        }
        return spProject;
    }

    @Override
    @TaskAnnotation("queryList")
    public List<SpProjectVO> queryList(SessionFactory factory, SpProjectQuery query) {
        // 对专项工程操作类型进行状态判断、排除id
        /*if (StringUtils.isEmpty(query.getProjectAction())) {
            // query.setStatus(null);
        } else {
            switch (query.getProjectAction()) {
                // 立项：规划中
                case Constants.SP_PROJECT_ACTION.ESTABLISH:
                    query.setStatus(Collections.singletonList(Constants.ProjectStatus.IN_PLAN));
                    query.setRids(spEstablishService.getRelated(factory));
                    break;
                // 项目后评价：已完成
                case Constants.SP_PROJECT_ACTION.END_APPRAISE:
                    query.setStatus(Collections.singletonList(Constants.ProjectStatus.FINISH));
                    query.setRids(spEndAppraiseService.getRelated(factory));
                    break;
                // 结算审查：进行中
                case Constants.SP_PROJECT_ACTION.SETTLEMENT_AUDIT:
                    query.setRids(spSettlementAuditService.getRelated(factory));
                    query.setStatus(Collections.singletonList(Constants.ProjectStatus.UNDERWAY));
                    break;
                // 其他：进行中
                default:
                    query.setStatus(Collections.singletonList(Constants.ProjectStatus.UNDERWAY));
                    break;
            }
        }
*/
        OrderByInterceptor.setOrderBy(query.getOrders());
        PageHelper.startPage(query.getPage(), query.getPageSize());
        // 根据状态查询
        // query.setSqlState(query.getState());
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        List<SpProjectVO> spProjectVOS = mapper.queryList(query);

        if (CollectionUtil.isNotEmpty(spProjectVOS)){
            spProjectVOS.forEach(s->{
                s.setMilestone(mapper.selectMilestone(s.getId(), null));
        //        s.setMember(CollectionUtil.newArrayList(mapper.selectMember(s.getId())));
            });
        }
        return spProjectVOS;
    }

    @Override
    @TaskAnnotation("page")
    public PageInfo<SpProjectVO> page(SessionFactory factory, SpProjectQuery query) {
        List<SpProjectVO> voList = queryList(factory, query);
        PageInfo<SpProjectVO> pageInfo = new PageInfo<>(voList);
        PageHelper.clearPage();
        String projectAction = query.getProjectAction();
        if (StringUtils.isEmpty(projectAction)) {
            return pageInfo;
        }
        List<SpProjectVO> spProjectVOS = listProjectRef(factory, pageInfo.getList().stream().map(SpProject::getId).collect(Collectors.toList()), projectAction);
        Map<String, Integer> collect = spProjectVOS.stream().collect(Collectors.toMap(SpProject::getId, SpProjectVO::getSpCount));
        for (SpProjectVO spProjectVO : pageInfo.getList()) {
            spProjectVO.setSpCount(collect.get(spProjectVO.getId()));
        }
        return pageInfo;
    }

    /**
     * 项目无法从新增，只能通过计划下发生成
     * @param factory
     * @param detail
     * @return
     */
    @Override
    @TaskAnnotation("saveOrUpdate")
    public SpProjectVO saveOrUpdate(SessionFactory factory, SpProjectVO detail) {
        Assert.notNull(detail, "专项计划不能为空");
        List<SpPlanMilestone> milestones = detail.getMilestone();
        //Assert.notEmpty(milestones, "里程碑不能为空");
        // 设置第一个里程碑为当前里程碑
        if(CollectionUtils.size(milestones) > 0) {
            detail.setCurrMilestone(StringUtils.isEmpty(detail.getCurrMilestone()) ? milestones.get(0).getName() : detail.getCurrMilestone());
        }
        if(StringUtils.isBlank(detail.getCode())) {
            // 设置专项台账编号
            String prefix = Constants.ProjectType.prefixMap.get(detail.getProjectType());
            String projectCode = CommonUtils.getCodeNo(prefix, 3);
            detail.setCode(projectCode);
        }
        // 1、专项计划主体（新增或更新）
        saveOrUpdate(factory, (SpProject) detail);
        // 2、协助部门
        SpAssistDeptMapper assistDeptMapper = factory.getMapper(SpAssistDeptMapper.class);
        List<SpAssistDept> assistDept = detail.getAssistDept();
        assistDeptMapper.deleteBySpId(detail.getId());
        if(!CollectionUtils.isEmpty(assistDept)) {
            assistDept.stream().forEach(row -> {
                row.setId(CodeTools.getCode32());
                row.setSpId(detail.getId());
                row.setCreateTime(new Date());
            });
            assistDeptMapper.addBatch(assistDept);
        }
        // 3、成员
        SpMemberMapper memberMapper = factory.getMapper(SpMemberMapper.class);
        List<SpMember> member = detail.getMember();
        memberMapper.deleteBySpId(detail.getId());
        if(!CollectionUtils.isEmpty(member)) {
            CommonUtils.sorted(member);
            member.stream().forEach(row -> {
                row.setId(CodeTools.getCode32());
                row.setSpId(detail.getId());
                row.setCreateTime(new Date());
            });
            memberMapper.addBatch(member);
        }
        // 4、里程碑
        SpPlanMilestoneMapper milestoneMapper = factory.getMapper(SpPlanMilestoneMapper.class);
        milestoneMapper.deleteBySpId(detail.getId());
        if(CollectionUtils.isNotEmpty(milestones)) {
            CommonUtils.sorted(milestones);
            for (int i = 0, size = milestones.size(); i < size; i++) {
                SpPlanMilestone milestone = milestones.get(i);
                // 第一个里程碑的计划开始时间为项目计划开始时间，之后每个里程碑的计划开始时间为上一个里程碑的计划结束时间
                milestone.setExpectStartTime(i == 0 ? detail.getPredictStartTime() : milestones.get(i - 1).getExpectEndTime());
                CommonUtils.update(milestone, ThreadLocalContext.get());
                milestone.setId(CodeTools.getCode32());
                milestone.setSpId(detail.getId());
                milestone.setCreateTime(new Date());
                // 默认未开始
                if (milestone.getStatus() == null) {
                    milestone.setStatus(Constants.MilestoneStatus.NOT_START);
                }
            }
            milestoneMapper.addBatch(milestones);
        }

        // 绑定设备
        spProjectEquipmentService.saveOrUpdateBatch(factory, detail);
        return detail;
    }

    @Override
    public SpProject saveOrUpdate(SessionFactory factory, SpProject detail) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        CommonUtils.update(detail);
        CommonUtils.update(detail, ThreadLocalContext.get());
        detail.setId(StringUtils.isEmpty(detail.getId()) ? CodeTools.getCode32() : detail.getId());
        detail.setCode(StringUtils.isEmpty(detail.getCode()) ? CommonUtils.getCodeNo(Constants.SP_PROJECT_PREFIX_CODE, 3) : detail.getCode());
        detail.setProgress(detail.getProgress() == null ? 0 : detail.getProgress());
        Assert.isTrue(detail.getProgress() >= 0 && detail.getProgress() <= 100, "进度值范围：[0, 100]");
        // 默认为规划中
        detail.setStatus(detail.getProgress() == 0 ? Constants.ProjectStatus.IN_PLAN : (detail.getProgress() == 100 && detail.getUpdateFlag() ? Constants.ProjectStatus.FINISH : Constants.ProjectStatus.UNDERWAY));
        mapper.saveOrUpdate(detail);
        return detail;
    }

    @Override
    @TaskAnnotation("saveOrUpdateBatch")
    public Boolean saveOrUpdateBatch(SessionFactory factory, List<SpProject> projectList) {
        if (CollectionUtils.isEmpty(projectList)) {
            return false;
        }
        EamUser eamUser = ThreadLocalContext.get();
        String orgPrefix = eamUser.getPrefix();
        //Assert.hasText(orgPrefix, "组织编码前缀不能为空");
        if(StringUtils.isBlank(orgPrefix)) {
            orgPrefix = "";
        }
        orgPrefix = StringUtils.upperCase(orgPrefix);
        String prefix = Constants.SP_PROJECT_PREFIX_CODE + orgPrefix;
        String key = eamUser.getAppId() + prefix;
        TOrderNoMax tOrderNoMax = tOrderNoMaxService.peekNoMax(key, "yyyyMMdd", projectList.size());
        AtomicInteger atomicInteger = new AtomicInteger(tOrderNoMax.getMaxValue() - projectList.size());
        projectList.forEach(detail -> {
            CommonUtils.update(detail);
            CommonUtils.update(detail, eamUser);
            detail.setId(StringUtils.isEmpty(detail.getId()) ? CodeTools.getCode32() : detail.getId());
            detail.setCode(StringUtils.isEmpty(detail.getCode()) ? prefix + tOrderNoMax.getDate() + String.format("%03d", atomicInteger.getAndIncrement()) : detail.getCode());
            detail.setProgress(detail.getProgress() == null ? 0 : detail.getProgress());
            Assert.isTrue(detail.getProgress() >= 0 && detail.getProgress() <= 100, "进度值范围：[0, 100]");
            // 默认为规划中
            detail.setStatus(detail.getProgress() == 0 ? Constants.ProjectStatus.IN_PLAN : (detail.getProgress() == 100 ? Constants.ProjectStatus.FINISH : Constants.ProjectStatus.UNDERWAY));
        });

        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.saveOrUpdateBatch(projectList) > 0;
    }

    @Override
    @TaskAnnotation("deleteById")
    public Boolean deleteById(SessionFactory factory, String id) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.hiddenById(id) > 0;
    }

    @Override
    @TaskAnnotation("getProjectDept")
    public List<OrgBean> getProjectDept(SessionFactory factory) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        List<String> projectDept = mapper.getProjectDept();
        List<SpProject> projectGroupDept = mapper.getProjectGroupDept();
        List<OrgBean> depts = projectGroupDept.stream().map(spProject -> {
            OrgBean orgBean = new OrgBean();
            orgBean.setId(spProject.getHostDeptId());
            String deptName = orgCacheUtils.getOrgNameById(spProject.getHostDeptId());
            orgBean.setName(StringUtils.isEmpty(deptName) ? spProject.getHostDeptName() : deptName);
            return orgBean;
        }).collect(Collectors.toList());
        return depts;
    }

    @Override
    @TaskAnnotation("getProjectOverview")
    public ProjectOverviewStat getProjectOverview(SessionFactory factory, Integer year, String hostDeptId) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        List<KeyValue> keyValues = mapper.getProjectOverview(year, hostDeptId);
        Map<String, Integer> defaultMap = new HashMap<>();
        defaultMap.put(Constants.ProjectStatus.IN_PLAN, 0);
        defaultMap.put(Constants.ProjectStatus.UNDERWAY, 0);
        defaultMap.put(Constants.ProjectStatus.FINISH, 0);
        Map<String, Integer> statMap = keyValues.stream().collect(Collectors.toMap(KeyValue::getLabel, KeyValue::getNum));
        defaultMap.putAll(statMap);
        Iterator<Map.Entry<String, Integer>> iterator = defaultMap.entrySet().iterator();
        List<KeyValue> keyValue = new ArrayList<>();
        while (iterator.hasNext()) {
            Map.Entry<String, Integer> next = iterator.next();
            keyValue.add(new KeyValue().setLabel(next.getKey()).setNum(next.getValue()));
        }
        ProjectOverviewStat stat = new ProjectOverviewStat().setKeyValue(keyValue);
        return stat;
    }

    @Override
    @TaskAnnotation("getProjectProgress")
    public Map<String, List<KeyValue>> getProjectProgress(SessionFactory factory, Integer year, String hostDeptId) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        Map<String, List<KeyValue>> map = new HashMap<>();
        // project
        List<SpProjectVO> projectList = mapper.getProjectProgress(year, hostDeptId, Constants.ProjectType.PROJECT);
        List<KeyValue> project = projectList.stream().map(row -> new KeyValue().setLabel(row.getName()).setNum(row.getProgress()).setStatus(row.getStatus())).collect(Collectors.toList());
        map.put(Constants.ProjectType.PROJECT, project);
        // purchase
        List<SpProjectVO> purchaseList = mapper.getProjectProgress(year, hostDeptId, Constants.ProjectType.PURCHASE);
        List<KeyValue> purchase = purchaseList.stream().map(row -> new KeyValue().setLabel(row.getName()).setNum(row.getProgress()).setStatus(row.getStatus())).collect(Collectors.toList());
        map.put(Constants.ProjectType.PURCHASE, purchase);
        return map;
    }

    @Override
    @TaskAnnotation("getProjectRef")
    public Integer getProjectRef(SessionFactory factory, String projectId, String projectAction) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.selectProjectRef(projectId, ProjectEnum.getTableName(projectAction));
    }

    @Override
    public List<SpProjectVO> listProjectRef(SessionFactory factory, List<String> projectId, String projectAction) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.listProjectRef(projectId, ProjectEnum.getTableName(projectAction));
    }

    @Override
    @TaskAnnotation("deleteByPlanId")
    public boolean deleteByPlanId(SessionFactory factory, String planId) {
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.deleteByPlanId(planId) > 0;
    }

    @Override
    @TaskAnnotation("update")
    public boolean update(SessionFactory factory, SpProject spProject){
        SpProjectMapper mapper = factory.getMapper(SpProjectMapper.class);
        return mapper.update(spProject);
    }
}
